import { _decorator, Button, CCFloat, CCInteger, Color, Component, Label, Node, Prefab, sp, SpriteComponent, SpriteFrame, Tween, v3 } from 'cc';
import { _2Node, LineConfig, LineNodeVo, SkConfig } from '../../gameCore/GOBERTS';
import { Slot } from '../../gameCore/bo/slot/Slot';
import { ClientEvent } from '../../framework/bo/ClientEvent';
import { EventName, PREFAB_PATH } from '../../Enum/Enum';
import GameConfig from '../../gameCore/GameConfig';
import { Util } from '../../framework/utils/util';
import { PoolManager } from '../../framework/manager/poolManager';
import BundleManager from '../../framework/manager/BundleManager';
const { ccclass, property } = _decorator;

@ccclass('SlotPanel')
export class SlotPanel extends Component {
    //===========配置属性====================
    @property({ type: Node, displayName: "转盘节点", tooltip: "转盘节点\n存放控制滚动的列节点" })
    public reelNodeArr: Node[] = [];
    @property({ type: SpriteFrame, displayName: "转盘图片", tooltip: "转盘图片\n按照数组顺序放置\n机器滚动的图标" })
    public sp: SpriteFrame[] = [];
    @property({ type: SpriteFrame, displayName: "奖励线条图片", tooltip: "奖励播放线条，需要在GameConfig中配置其它属性(位置、颜色)" })
    public lineSp: SpriteFrame[] = [];

    @property({ type: CCInteger, displayName: "最高滚速", tooltip: "机器达到的最高滚动速度 25、45、65", min: 10, max: 80, step: 5, slide: true })
    public speed_max: number = 75;// 最大滚动速度 最高80,太高不好了
    @property({ type: CCFloat, displayName: "滚动时长", tooltip: "机器滚动之后持续的一定时间后停止滚动", min: 0.1, max: 5.0, step: 0.1, slide: true })
    public roll_time: number = 0.4;// 滚动时间
    @property({ type: sp.SkeletonData, displayName: "骨骼动画数据", tooltip: "配置骨骼动画数据后，\n需要根据情况在代码中配置对应图标名称、缩放、偏移等信息\n并在调用时做相应修改" })
    public skData: sp.SkeletonData[] = [];
    @property({ type: Node, displayName: "骨骼动画面板", tooltip: "指定骨骼动画播放面板" })
    public skPanelNode: Node = null!;

    //====================控制按钮或其它========================
    @property({ type: Button, displayName: "开始滚动控制按钮", tooltip: "开始滚动控制按钮\nspin点击开始滚动" })
    public btn_spin: Button = null!;
    @property({ type: Button, displayName: "自动滚动按钮", tooltip: "自动开始滚动,按下之后取消自动滚动" })
    public btn_auto: Button = null!;
    @property({ type: CCInteger, displayName: "自动滚动延迟(ms)", tooltip: "自动滚动延迟时间: 在延迟指定时间之后再次开始自动开始滚动" })
    public auto_spin_time: number = 2000;// 自动滚动延迟时间
    //停止所有列状态按钮
    @property({ type: Button, displayName: "缓动停止单列按钮", tooltip: "节点激活后，单列间隔轮流停止滚动" })
    public btn_fast: Button = null!;
    @property({ type: Button, displayName: "停止所有列按钮", tooltip: "激活后，所有列一起停止滚动" })
    public btn_fast_checked: Button = null!;
    @property({ type: Label, displayName: "金币Lable节点", tooltip: "控制游戏币Lable节点" })
    public lable_coin1: Label = null!;
    @property({ type: Label, displayName: "代币Lable节点", tooltip: "控制充值的代币Lable节点" })
    public lable_coin2: Label = null!;
    @property({ type: Label, displayName: "押注倍率 TOTAL BET", tooltip: "控制押注显示的Lable节点" })
    public lable_bet: Label = null!;
    @property({ type: Label, displayName: "得分 TOTAL WIN", tooltip: "控制得分的Lable节点" })
    public lable_win: Label = null!;

    //==============================私有配置变量 不支持随意修改============================
    protected _canSpin: boolean = true// 可以调用滚动功能
    protected _speed: number = 0;// 实时滚速
    protected _rollFlag: boolean = false;// 正在滚动
    protected _stoping: boolean = false;// 正在停止滚动
    //滚动结束刷新最后结果图标标识
    protected _refresh_last_result: boolean[] = [];// 刷新最后结果图标标识 缺少则会影响结果显示
    protected readonly _reelHight: number = 410;// 单列卷轴滚轮总高度 影响着滚动的起始位置和停止位置
    protected _stopIndex: number = 0;// 停止位置 单条滚动接力停止使用
    protected _stopIndex_temp: number[] = [];// 临时停止位置 单条滚动接力停止使用 // 表示已经停止滚动
    // private _tween_roll_start: Tween<Slot01Panel>;// 开始的滚动缓动函数 弃用 游戏规则没有允许玩家在刚开始spin时立即停止滚动，而是在一定时间后自动调用停止滚动函数，所以弃用了
    protected _tween_roll_stop: Tween<SlotPanel>;// 停止单列滚动所使用的缓动函数与stopIndex配合使用
    protected _machineId: string = "01"//机器编号，初始化数据时使用，为了初始化对应的机器数据
    protected _sk_node_temp: _2Node[] = [];// 临时存储骨骼动画节点 在播放结束后回收节点到poolManager
    protected _fast_flag = false//直接停止所有列判断
    // 结算完成后，动画播放所需状态变量
    protected _shine_in_interval = 0.4//缓动闪烁得分图标间隔
    protected _shine_out_interval = 0.6//缓动闪烁得分图标间隔
    protected _shine_line_node_change_interval = 2//切换缓动“奖励线节点”间隔
    protected _shine_line_node_index = 0//缓动闪烁奖励线当前位置
    protected _reward_line_node_temp: LineNodeVo[] = []// 滚动结束后，结算出的所有奖励线节点
    protected _tween_line_node_temp: Tween<SlotPanel>// 滚动结束后，缓动播放奖励线节点闪烁或动画
    protected _tween_shine_line_node_temp: Tween<SlotPanel>// 滚动结束后，缓动播放奖励线节点闪烁或动画
    //机器数据对象
    protected slotVo: Slot = null;//继承 Slot
    // private _tween_coin1_lable_temp = null// 缓动播放金币动画 改帧驱动 已弃用

    protected _update_fun_map: Map<string, Function> = new Map<string, Function>()// 由每帧调用的功能临时存放处
    // private scheduler = director.getScheduler();// cocos定时器

    show() {
        console.log(this.node.name + " show");
        ClientEvent.on(EventName.Spin_end, this.onSpinEnd, this)
        GameConfig.instance.initMachine(this._machineId)
        this.btnChangeFast()
        this.slotVo = new Slot(this);
        this.slotVo.init();
    }

    protected onDisable(): void {
        ClientEvent.off(EventName.Spin_end, this.onSpinEnd, this)
    }

    btnChangeFast() {//按钮功能 修改状态 单列轮流停止滚动或者所有列一起停止滚动
        this._fast_flag = !this._fast_flag;
        this.btn_fast.node.active = !this._fast_flag;
        this.btn_fast_checked.node.active = this._fast_flag;
    }
    btnChangeButton() {//触发一个长按事件 切换滚动按钮
        const flag = this.btn_spin.node.active
        this.btn_spin.node.active = !flag
        this.btn_auto.node.active = flag
        // console.log("btnChangeButton this.btn_spin.node.active", this.btn_spin.node.active);
        if (flag) // 判断是否自动滚动
            this.autoSpin();
    }
    protected autoSpin() {
        if (this.btn_auto.node.active) {
            if (!this._rollFlag) { //判断是否正在滚动
                console.log("auto start");
                this.btnRoll() //否 调用滚动
            }
        }
    }
    btnRoll() {//spin按钮触发函数
        if (this._canSpin)//添加新状态 禁止旋转
            this.RollFlag = !this._rollFlag;
    }
    btnAdd() {// 按钮触发 增加押注
        if (this.slotVo.MyBet < Slot.MaxBet)
            this.slotVo.MyBet += Slot.BaseBet
    }
    btnReduce() {// 按钮触发 减少押注
        if (this.slotVo.MyBet > Slot.MinBet)
            this.slotVo.MyBet -= Slot.BaseBet
    }
    btnMaxBet() {// 按钮触发 设置最大押注
        if (this.slotVo.MyBet < Slot.MaxBet)
            this.slotVo.MyBet = Slot.MaxBet
        else {
            this.slotVo.MyBet = Slot.MinBet
        }
    }

    /**
 * 此为绑定事件
 * 机器滚动结束后 做游戏结算 并在此调用播放结算动画
 */
    public onSpinEnd() {
        console.log('播放结算动画');
        this.slotVo.updateSelfRequestDataForUI()// 填充网络请求数据
        this.playAnimation();
        this.setBtnSpinLable(true)
        //自动滚动
        if (this.btn_auto.node.active) {
            console.log("定时自动滚动(ms):", this.auto_spin_time)
            setTimeout(() => { this.autoSpin() }, this.auto_spin_time)
        }

    }

    //================================ 希望继承的方法======================================
    /**
     * 开始滚动 刷新滚动结果
     */
    flashResultData() {
        Util.httpGET('http://192.168.31.203:3000/api_t1/spin', (val) => {  // 网络请求 
            this.slotVo.Result(val);// 设置结果
        }, { wager: this.slotVo.MyBet / 50 + '' })//wager是bet（基础倍率50）接口请求所需
    }
    /** 完整滚动方法
  * @param val 停或开始滚动 滚动功能由帧驱动 update
  */
    public set RollFlag(val: boolean) {
        if (val) {
            console.log('开始滚动');
            this.resetAllProps() // 重置所有属性
            this._rollFlag = val;//使得update开始调用滚动
            let tween = new Tween();// 对速度缓动 递增
            tween.target(this);
            tween.to(this.roll_time / 4, { _speed: this.speed_max })//this._tween_roll_start =
                .delay(this.roll_time)// 延迟一定时间后自动停止滚动
                .call(() => {
                    this.RollFlag = false;// 调用停止滚动
                })
                .start();
            this.btn_spin.interactable = false
            this.flashResultData()// 网络请求 获取滚动结果
        } else {
            // console.log('停止滚动');
            if (this._stoping) {//停止所有滚动列的滚动
                // console.log('停止所有滚动列的滚动');
                this.btn_spin.interactable = false
                this._tween_roll_stop.stop()
                this._stopIndex = this.reelNodeArr.length - 1//立即停止到最后一列
                // console.log('stopIndex', this._stopIndex, this.reelNodeArr.length - 1);
            } else {// 单列滚动接替停止,使用缓动
                // console.log('使用缓动,单列滚动接替停止');
                this._stoping = true;
                if (this._fast_flag) {// 停止所有列判断
                    this._stopIndex = this.reelNodeArr.length - 1
                }
                else {
                    this._stopIndex = 0;
                    let tween = new Tween().target(this);
                    this._tween_roll_stop = tween
                        .repeat(this.reelNodeArr.length, tween.delay(0.5)
                            .call(() => {
                                // console.log('stopIndex', this.stopIndex);
                                this._stopIndex++
                            }))
                        .start();
                    this.setBtnSpinLable(false)
                }
            }
        }
    }
    /**
     * 重置属性 按动spin按钮旋转时触发
     */
    protected resetAllProps() {
        this._tween_line_node_temp?.stop()//缓动奖励线
        this._tween_shine_line_node_temp?.stop()//缓动闪烁
        this.resetRewardLineNode(this._shine_line_node_index, true);
        this._shine_line_node_index = 0;
        this._reward_line_node_temp = []
        this.cleanSkAniTemp()
        this._stopIndex_temp = [];// 重置滚动单列临时停止位置
        this._speed = 0;// 重置速度为0 为了执行缓动加速

        this.slotVo.clean()//清理网络请求的结果
        this._refresh_last_result = []// 重置ui面板的网络请求结果
    }

    /**
     * 播放动画
     * 游戏单局结束之后触发
     * 在骨骼动画播放面板对得分节点创建骨骼动画并播放
     * 没有骨骼动画的使用缓动播放闪烁
     */
    public playAnimation() {
        // 正常游戏模式
        const rewardLineNodeArr: LineNodeVo[] = this.getNodeArrByBack()//this.getNodeArr();// 挑选出奖励线数组
        if (rewardLineNodeArr.length <= 0)//没有奖励
            return
        this._reward_line_node_temp = rewardLineNodeArr;
        // 缓动轮流播放奖励线上的节点
        this._tween_line_node_temp?.stop();
        let tween: Tween<SlotPanel> = new Tween(this);
        let tween2: Tween<SlotPanel> = new Tween(this)

        tween2 = tween2// 缓动闪烁节点
            .call(() => {
                this.shineLineNode(false);
            })
            .delay(this._shine_in_interval)
            .call(() => {
                this.shineLineNode(true);
            })
            .delay(this._shine_out_interval)
            .union().repeat(3);

        this._tween_line_node_temp = tween.call(() => {// 有动画的播放动画，没动画的缓动播放闪烁 TODO
            this._tween_shine_line_node_temp?.stop();
            this._tween_shine_line_node_temp = tween2.start();
            this.cleanSkAniTemp()
            if (this._reward_line_node_temp[this._shine_line_node_index].isSkAni) {
                // 缓动播放骨骼动画
                this._reward_line_node_temp[this._shine_line_node_index].nodes.forEach((val) => {
                    this.playAnimationSingle(val); //播放单个节点对应的骨骼动画
                })
            }
        })
            .delay(this._shine_line_node_change_interval)//延迟之后切换奖励线
            .call(() => {
                this._shine_line_node_index++//切换到下一个奖励线
                this._shine_line_node_index = this._shine_line_node_index % this._reward_line_node_temp.length;
                //重置上一个奖励线
                this.resetRewardLineNode(this._shine_line_node_index - 1 < 0 ? this._reward_line_node_temp.length - 1 : this._shine_line_node_index - 1)
                // console.log("shine_index", this._shine_line_node_index, "size", this._reward_line_node_temp.length);
            })
            .union().repeat(this._reward_line_node_temp.length * 6).call(() => { this.cleanSkAniTemp() }).start();

    }
    protected async playAnimationSingle(val: Node) {
        const ico_name = this.getSpriteName(val)  // console.log("图标名称", ico_name);
        const config: SkConfig = GameConfig.instance.animation_config.get(this._machineId + ico_name)
        if (config) {   //判断是否有动画
            //获取带有骨骼动画组件的预制体 // console.log("播放骨骼动画", config);
            const prefab: unknown = await BundleManager.Instance.loadPrefab(config.prefab_path ? config.prefab_path : PREFAB_PATH.sk_ani_prefab)
            const sk_node: Node = PoolManager.instance.getNode(prefab as Prefab, this.skPanelNode, ico_name); //依据ico_name 用预制体获取节点
            sk_node.setWorldPosition(val.getWorldPosition()); //修改节点坐标为图标对应位置并把图标节点隐藏
            const skclip: sp.SkeletonData = this.getSkClip(config.sk_name) //本场景加载好的骨骼动画数据
            if (skclip) { //获取骨骼动画组件 配置属性
                sk_node.setWorldPosition(sk_node.worldPosition.x + config.sk_offsetX, sk_node.worldPosition.y + config.sk_offsetY, 0);
                sk_node.setScale(config.sk_scaleX, config.sk_scaleY);
                let com: sp.Skeleton = sk_node.getComponent(sp.Skeleton)
                com.skeletonData = skclip;
                val.active = false;// 在播放动画时，隐藏被动画覆盖的图标
                com.animation = config.sk_ani_name;//播放动画
                this._sk_node_temp.push({ skNode: sk_node, icoNode: val });
            }
        }
    }
    /**
    * 从后端数据提取得分节点
    * @returns LineNodeVo[]
    */
    public getNodeArrByBack() {
        let nodeArr: LineNodeVo[] = [];
        if (this.slotVo.PayLines)
            for (let i = 0; i < this.slotVo.PayLines.length; i++) {
                let lineNode: LineNodeVo = { nodes: [], lineNodes: [], isSkAni: false, lineConfigIndex: i };
                for (let j = 0; j < this.slotVo.PayLines[i].length; j++) {
                    const icoNode = this.reelNodeArr[j].getChildByName("r2").children[this.slotVo.PayLines[i][j]]
                    lineNode.nodes.push(icoNode)//放入得分节点 。。
                    if (j == 0) {
                        const ico_name = this.getSpriteName(icoNode)
                        const config: SkConfig = GameConfig.instance.animation_config.get(this._machineId + ico_name)
                        lineNode.isSkAni = config ? true : false;// 判断图标是否有动画
                    }
                }
                nodeArr.push(lineNode);
            }
        console.log("得分线条数", nodeArr.length);
        return nodeArr;
    }
    /**
    * 调用 货币等 ui数值 缓动变化 帧驱动 
    * 出现其它数值需要变化时可继承此方法重写
    * @param coin_index 动画播放的货币类型 1 -> coin1 | 2 -> coin2
    */
    public tweenLableFun(coin_index: number) {
        if (coin_index == 1) {// 分开货币1、2 。。。来写代码更简便易理解
            // console.log("coin1");
            this._update_fun_map.delete('coin1LableFun')
            this._update_fun_map.set('coin1LableFun', (dt) => {
                const val = this.slotVo.Coin1;
                const startValue = parseInt(this.lable_coin1.string);
                const endVal = val - startValue;
                if (endVal > 100) {
                    const v = Math.floor(Util.lerp(val, startValue, 0.05)) + ''
                    // console.log(v);
                    this.lable_coin1.string = v
                }
                else {
                    this._update_fun_map.delete('coin1LableFun')
                    this.lable_coin1.string = this.slotVo.Coin1 + '';
                }
            });
            setTimeout(() => {
                // console.log("delete coinLableFun")
                if (this._update_fun_map.delete('coin1LableFun'))
                    this.lable_coin1.string = this.slotVo.Coin1 + '';
            }, 2000)
        } else if (coin_index == 2) {//代币
            // console.log("coin1");
            this._update_fun_map.delete('coin2LableFun')
            this._update_fun_map.set('coin2LableFun', (dt) => {
                const val = this.slotVo.Coin2;
                const startValue = parseInt(this.lable_coin2.string);
                const endVal = val - startValue;
                if (endVal > 100) {
                    const v = Math.floor(Util.lerp(val, startValue, 0.05)) + ''
                    // console.log(v);
                    this.lable_coin2.string = v
                }
                else {
                    this._update_fun_map.delete('coin2LableFun')
                    this.lable_coin2.string = this.slotVo.Coin2 + '';
                }
            });
            setTimeout(() => {
                if (this._update_fun_map.delete('coin2LableFun'))
                    this.lable_coin2.string = this.slotVo.Coin2 + '';
            }, 2000)
        } else if (coin_index == 3) {// 押注
            this._update_fun_map.delete('betLableFun')
            this._update_fun_map.set('betLableFun', (dt) => {
                const val = this.slotVo.MyBet;
                const startValue = parseInt(this.lable_bet.string);
                const endVal = val - startValue;
                if (endVal > 10 || endVal < -10) {
                    const v = Math.floor(Util.lerp(val, startValue, 0.2)) + ''
                    this.lable_bet.string = v
                }
                else {
                    this._update_fun_map.delete('betLableFun')
                    this.lable_bet.string = this.slotVo.MyBet + '';
                }
            })
            setTimeout(() => {
                this.lable_bet.string = this.slotVo.MyBet + '';
            }, 600)
        } else if (coin_index == 4) {// 得分
            this._update_fun_map.delete('winLableFun')
            this._update_fun_map.set('winLableFun', (dt) => {
                const val = this.slotVo.Win;
                const startValue = parseInt(this.lable_win.string);
                const endVal = val - startValue;
                if (endVal > 50) {
                    const v = Math.floor(Util.lerp(val, startValue, 0.05)) + ''
                    this.lable_win.string = v
                }
                else {
                    this._update_fun_map.delete('winLableFun')
                    this.lable_win.string = this.slotVo.Win + '';
                }
            });
            setTimeout(() => {
                if (this._update_fun_map.delete('winLableFun'))
                    this.lable_win.string = this.slotVo.Win + '';
            }, 1500)
        }
    }
    /**
     * 根据节点中的configPos属性配置奖励线图标
     * 
     * @param nodeArr 
     */
    protected async initRewardLine(rewardLineNode: LineNodeVo) {
        //rewardLineNode.lineConfigIndex console.log("线条配置属性加载");
        let lineConfig: LineConfig = GameConfig.instance.reward_line_config[Util.randomInt(GameConfig.instance.reward_line_config.length)];
        if (this.slotVo.PayLines)// 刷新后端收到的线条位置
            lineConfig.configPos = this.slotVo.PayLines[rewardLineNode.lineConfigIndex]
        const prefab: unknown = await BundleManager.Instance.loadPrefab(PREFAB_PATH.LINE_NODE_PREFAB)//获取奖励线预制体

        for (let i = 0; i < lineConfig.configPos.length; i++) {// 配置单根线条的每一条线段
            let ico_name: string = "rewardLine" + i;
            const lineNode: Node = PoolManager.instance.getNode(prefab as Prefab, this.skPanelNode, ico_name); //获取奖励线节点
            //配置线条属性
            // 设置位置
            const y: number = this.getLineNodeYPos(lineConfig.configPos[i], i == 0 ? -1 : lineConfig.configPos[i - 1])
            lineNode.setPosition(GameConfig.instance.reward_line_x[i], y)
            const lableNode: Node = lineNode.getChildByName("Label")
            lableNode.active = false
            //设置线条精灵图
            const spCom: SpriteComponent = lineNode.getComponent(SpriteComponent)
            const spIndex: number = i == 0 ? 2 : lineConfig.configPos[i] - lineConfig.configPos[i - 1] + 2// 针对3*5 console.log("线条图标索引", spIndex);
            spCom.spriteFrame = this.lineSp[spIndex]
            // 设置线条颜色
            spCom.color = new Color(lineConfig.color)
            rewardLineNode.lineNodes.push(lineNode)
        }
        //设置线条标头
        let ico_name: string = "rewardLineHead";
        const lineNode: Node = PoolManager.instance.getNode(prefab as Prefab, this.skPanelNode, ico_name); //获取奖励线节点
        const spIndex: number = 5
        const y: number = this.getLineNodeYPos(lineConfig.configPos[0], -1)
        lineNode.setPosition(GameConfig.instance.reward_line_x[0] - 18, y)// 设置位置
        const spCom: SpriteComponent = lineNode.getComponent(SpriteComponent)
        spCom.spriteFrame = this.lineSp[spIndex]//设置图标
        const lableNode: Node = lineNode.getChildByName("Label")
        lableNode.active = true
        lableNode.getComponent(Label).string = rewardLineNode.lineConfigIndex + 1 + ""
        // 配置线条图标属性
        spCom.color = new Color(lineConfig.color)
        rewardLineNode.lineNodes.push(lineNode)

    }
    /**获取线条图片所需y轴位置的算法 仅考虑3*5图标阵列计算 arg为列序号 
     * 当出现不是3*5机器时可以重写此方法
     * */
    protected getLineNodeYPos(arg0: number, arg1: number): number {
        if (arg0 - arg1 == 0 || arg1 == -1) {
            // console.log("arg0", arg0);
            return GameConfig.instance.reward_line_y[arg0]
        }
        if (Math.abs(arg0 - arg1) == 1) {
            return arg0 == 2 ? -GameConfig.instance.reward_line_y[3]
                : arg0 == 0 ? GameConfig.instance.reward_line_y[3]
                    : (arg0 - arg1) * GameConfig.instance.reward_line_y[3]
        }
        return 0;
    }
    //希望继承的方法

    // ==================================滚动核心方法==================================
    update(dt: number): void {
        if (this._rollFlag) {
            this.roll()
        }
        if (this._stoping) {
            if (this.checkStopPosition()) {
                // console.log("stop start",this._stoping);
                this.rollToStop()
            }
        }
        if (this._update_fun_map.size > 0) {
            for (let key of this._update_fun_map.keys()) {
                this._update_fun_map.get(key)(dt)
            }
        }
        // console.log("_stoping",this._stoping);
    }
    // 检查是否符合停止滚动位置
    protected checkStopPosition(): boolean {
        if (!this.slotVo.StopIcon) {  // 判断网络请求结果是否收到 false
            return false
        }

        for (let i = 0; i < this.reelNodeArr.length; i++) {
            if (this._stopIndex_temp[i]) {// 表示已经停止滚动
                continue
            }
            if (!this._refresh_last_result[i]) { //判断是否刷新网络请求的结果图标
                return false;
            }
            let nd: Node = this.reelNodeArr[i];
            let r2: Node = nd.getChildByName("r2")
            if (r2.position.y < 0 || r2.position.y > this.speed_max + 10) {//  1帧时间范围，临近停止的y轴坐标范围
                return false;
            }
        }
        return true;
    }

    /**
    * 滚动方法 每帧调用
    * 调用滚轮所在节点向下移动一次 speed设置速度
    */
    protected roll() {
        // console.log('滚动列数', this.reelNodeArr.length);
        for (let i = 0; i < this.reelNodeArr.length; i++) {

            if (this._stopIndex_temp[i]) {// 表示已经停止滚动
                // console.log('stopIndex_temp 停止', this._stopIndex_temp[i]);
                continue
            }
            let nd: Node = this.reelNodeArr[i]
            let r1: Node = nd.getChildByName("r1")
            let r2: Node = nd.getChildByName("r2")
            // console.log('滚动速度', this._speed);
            r1.setPosition(r1.position.x, r1.position.y - this._speed)
            r2.setPosition(r2.position.x, r2.position.y - this._speed)
            // console.log('r1.position.y', r1.position.y);
            if (r1.position.y <= -this._reelHight) {
                r1.setPosition(r1.position.x, this._reelHight);
                for (let i = 0; i < r1.children.length; ++i) {
                    this.refrashIco(r1.children[i])
                }

            }
            if (r2.position.y <= -this._reelHight) {
                r2.setPosition(r2.position.x, this._reelHight);
                for (let j = 0; j < r2.children.length; ++j) {
                    // console.log("stop", this._stoping);
                    this.refrashIco(r2.children[j], this._stoping && this.slotVo.StopIcon != null, i, j)
                    if (this._stoping && this.slotVo.StopIcon) {
                        this._refresh_last_result[i] = true
                    }
                }
            }
        }

    }
    protected rollToStop() {// 停止滚动
        for (let i = 0; i < this.reelNodeArr.length; i++) {
            if (this._stopIndex_temp[i]
                || i > this._stopIndex) {// 表示已经停止滚动
                continue
            }
            this._stopIndex_temp[i] = 1// 标识当前列已经停止滚动
            this._stoping = this._rollFlag = this.rollContinue();
            const nd: Node = this.reelNodeArr[i];
            let r1: Node = nd.getChildByName("r1")
            let r2: Node = nd.getChildByName("r2")
            if (r1.position.y >= 0) {// bug 解决出现滚动过头的情况
                r1.position = v3(r1.position.x, -this._reelHight, 0)
            } else {
                let tween = new Tween();
                tween.target(r1);
                const time: number = (r1.position.y + this._reelHight) / this._speed / 60
                // console.log("时间", time, r1.position.y);
                tween.to(time, { position: v3(r1.position.x, -this._reelHight, 0) })
                    .start();
            }

            let tween2 = new Tween();
            tween2.target(r2);
            const time2: number = r2.position.y / this._speed / 60
            // console.log("时间2", time2, r2.position.y);
            const interval_time: number = 0.1 < this.roll_time ? 0.1 : this.roll_time / 2;
            // let interval: number = Math.ceil(this.speed * GameConfig.fram_rate * interval_time)
            const interval = this._speed >= 25 ? this._speed >= 45 ? this._speed >= 65 ? 45 : 35 : 25 : 15;
            // console.log("间隔时间", interval_time, interval);
            tween2.to(time2, { position: v3(r1.position.x, 0, 0) })
                .by(interval_time, { position: v3(0, -interval, 0) })
                .by(interval_time, { position: v3(0, interval, 0) })
                .call(() => {
                    if (i == this.reelNodeArr.length - 1)// 最后一列滚动停止
                        ClientEvent.dispatchEvent(EventName.Spin_end)// 提交滚动事件结束，开始结算动画
                })// 恢复滚动按钮功能按钮
                .start();
        }
    }
    /**
   * 判断所有列滚动是否都停止
   * @returns 是否继续滚动
   */
    protected rollContinue() {
        if (this._stopIndex_temp.length != this.reelNodeArr.length)
            return true;
        return false;
    }
    /**
   * 刷新图标私有方法 刷新最后图片入口函数，在此调用修改最后结果图标
   * @param [isEnd=false] 是否刷新最终结果图标,依据正在机器状态是否为正在停止做判断
   * @param x 节点图标所在列序号
   * @param y 节点图标所在行序号
   * */
    protected refrashIco(iconNd: Node, isEnd: boolean = false, x?: number, y?: number) {
        const spCom: SpriteComponent = iconNd.getComponent(SpriteComponent)
        if (isEnd) {
            // console.log("刷新最后结果图标", x, y, this.slot01Vo.StopIcon[y][x]);
            const i = GameConfig.instance.img_name.get(this.slotVo.StopIcon[y][x])
            spCom.spriteFrame = this.sp[i];
            return;
        }
        const i = Util.getRandomInt(0, this.sp.length - 1)
        spCom.spriteFrame = this.sp[i];
    }
    // 滚动核心方法end



    //=============================私有方法==============================
    /**
    * 私有方法 闪烁功能基础函数 显示或隐藏this._shine_line_node_index所指向的奖励节点
    * @param flag 显示或隐藏
    * @returns 
    */
    private shineLineNode(flag: boolean) {
        let lineNodes = this._reward_line_node_temp[this._shine_line_node_index].lineNodes;
        // console.log("lineNodes", lineNodes);
        if (lineNodes.length <= 0) {
            this.initRewardLine(this._reward_line_node_temp[this._shine_line_node_index]);
        }
        this._reward_line_node_temp[this._shine_line_node_index].lineNodes.forEach((lineNode) => {
            lineNode.active = flag;// 奖励线 闪烁
        })

        if (this._reward_line_node_temp[this._shine_line_node_index].isSkAni)
            return;// 如果是骨骼动画，则不闪动，通过其他缓动去调用动画即可
        // if (!flag) {  console.log("缓动闪烁in", this._shine_line_node_index);} else { console.log("缓动闪烁out", this._shine_line_node_index);}
        let nodes = this._reward_line_node_temp[this._shine_line_node_index].nodes
        nodes.forEach((node) => {
            node.active = flag// 节点闪烁
        })

    }
    /** 清理骨骼动画临时播放节点*/
    private cleanSkAniTemp() {
        this._sk_node_temp.forEach((val) => {
            val.icoNode.active = true
            PoolManager.instance.putNode(val.skNode) //回收骨骼动画播放节点
        })
        this._sk_node_temp = [];
    }
    /**
     * 重置奖励线，当缓动切换时或开始滚动时，需要清理上一个缓动对节点的影响
     * @param index 受影响的奖励线引用位置
     * @param recycle 回收lineNode节点
     */
    private resetRewardLineNode(index: number, recycle: boolean = false) {
        this._reward_line_node_temp[index]?.nodes.forEach((val) => {
            val.active = true
        })
        this._reward_line_node_temp[index]?.lineNodes.forEach((val) => {
            val.active = false
            if (recycle) {
                // console.log('回收lineNode');
                PoolManager.instance.putNode(val)
            }
        })

    }
    /**
    * 设置滚动按钮文本内容
    * @param val true则设置内容为spin false则设置内容为stop
    */
    protected setBtnSpinLable(val: boolean) {
        if (val) {
            this.btn_spin.node.getChildByName("Label").getComponent(Label).string = "SPIN";
        } else {
            this.btn_spin.node.getChildByName("Label").getComponent(Label).string = "STOP";
        }
        this.btn_spin.interactable = true
    }
    public getSpriteName(node: Node) {
        return node?.getComponent(SpriteComponent)?.spriteFrame.name;
    }

    private getSkClip(sk_name: string): sp.SkeletonData {
        let clip: sp.SkeletonData = null;
        for (let i = 0; i < this.skData.length; ++i) {
            let temp: sp.SkeletonData = this.skData[i];
            if (temp.name == sk_name) {
                clip = temp;
                break;
            }
        }
        return clip;
    }
    //私有方法

}

